﻿using Engine.FileFormats3D;
using Engine.Graphics;
using Engine.Graphics.Shaders;
using Engine.Graphics.VertexStructures;
using Engine.Helpers;
using Engine.ObjectManager;
using SharpDX;
using SharpDX.Direct3D11;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace Engine.DynamicObjectManager
{
    [StructLayout(LayoutKind.Explicit, Size = 96)]
    public struct ObjectInstance
    {
        /// <summary>
        /// Position of the object on terrain.
        /// </summary>
        [FieldOffset(0)]
        public Vector4 position;

        /// <summary>
        /// Rotation matrix for the object.
        /// </summary>
        [FieldOffset(16)]
        public Matrix rotatationY;

        /// <summary>
        /// Scales the objects.
        /// </summary>
        [FieldOffset(80)]
        public float scale;

        public static ObjectInstance FromStream(BinaryReader br)
        {
            string ident = new string(br.ReadChars(3));
            if (ident == "OIN")
            {
                switch (br.ReadByte())
                {
                    case 1:
                        return LoadV1(br);
                }
            }
            throw new Exception("Unsupported fileFormat");
        }

        private static ObjectInstance LoadV1(BinaryReader br)
        {
            ObjectInstance inst = new ObjectInstance();

            inst.position.X = br.ReadSingle();
            inst.position.Y = br.ReadSingle();
            inst.position.Z = br.ReadSingle();
            inst.position.W = br.ReadSingle();

            inst.scale = br.ReadSingle();

            inst.rotatationY.M11 = br.ReadSingle();
            inst.rotatationY.M12 = br.ReadSingle();
            inst.rotatationY.M13 = br.ReadSingle();
            inst.rotatationY.M14 = br.ReadSingle();

            inst.rotatationY.M21 = br.ReadSingle();
            inst.rotatationY.M22 = br.ReadSingle();
            inst.rotatationY.M23 = br.ReadSingle();
            inst.rotatationY.M24 = br.ReadSingle();

            inst.rotatationY.M31 = br.ReadSingle();
            inst.rotatationY.M32 = br.ReadSingle();
            inst.rotatationY.M33 = br.ReadSingle();
            inst.rotatationY.M34 = br.ReadSingle();

            inst.rotatationY.M41 = br.ReadSingle();
            inst.rotatationY.M42 = br.ReadSingle();
            inst.rotatationY.M43 = br.ReadSingle();
            inst.rotatationY.M44 = br.ReadSingle();
            return inst;
        }

        internal void ToStream(BinaryWriter bw)
        {
            bw.Write("OIN".ToCharArray());
            bw.Write((byte)1); // VersionNumber

            // Position
            bw.Write(this.position.X);
            bw.Write(this.position.Y);
            bw.Write(this.position.Z);
            bw.Write(this.position.W);

            // Scale
            bw.Write(this.scale);

            // Rotation Matrix
            bw.Write(this.rotatationY.M11);
            bw.Write(this.rotatationY.M12);
            bw.Write(this.rotatationY.M13);
            bw.Write(this.rotatationY.M14);

            bw.Write(this.rotatationY.M21);
            bw.Write(this.rotatationY.M22);
            bw.Write(this.rotatationY.M23);
            bw.Write(this.rotatationY.M24);

            bw.Write(this.rotatationY.M31);
            bw.Write(this.rotatationY.M32);
            bw.Write(this.rotatationY.M33);
            bw.Write(this.rotatationY.M34);

            bw.Write(this.rotatationY.M41);
            bw.Write(this.rotatationY.M42);
            bw.Write(this.rotatationY.M43);
            bw.Write(this.rotatationY.M44);
        }
    }

    public class ObjectsProperties : IDisposable
    {
        #region Public Fields

        public Engine engine;

        /// <summary>
        /// Instancing buffer where position and angle is stored in.
        /// </summary>
        public SharpDX.Direct3D11.Buffer instanceBuffer;

        /// <summary>
        /// Mox-File for the object.
        /// </summary>
        public MoxFile moxFile;

        /// <summary>
        /// Object instancing data.
        /// </summary>
        public Dictionary<BaseNode, ObjectInstance> objectInstances;

        public VertexBufferBinding vertexBufferBinding;

        #endregion Public Fields

        #region Private Fields

        private BufferDescription vertexBufferDescription;

        #endregion Private Fields

        #region Public Constructors

        public ObjectsProperties(Engine engine)
        {
            this.objectInstances = new Dictionary<BaseNode, ObjectInstance>();
            this.engine = engine;

            this.vertexBufferDescription = new BufferDescription()
            {
                BindFlags = BindFlags.VertexBuffer,
                CpuAccessFlags = 0,
                // OptionFlags = ResourceOptionFlags.BufferStructured,
                SizeInBytes = 0,
                Usage = ResourceUsage.Default,
                StructureByteStride = Marshal.SizeOf(typeof(ObjectInstance))
            };
        }

        #endregion Public Constructors

        #region Public Methods

        public static ObjectsProperties FromStream(BinaryReader br, Engine engine, string pathToMoxFile, TextureManager textureManager)
        {
            string ident = new string(br.ReadChars(3));
            if (ident == "INS")
            {
                switch (br.ReadByte())
                {
                    case 1:
                        return LoadV1(br, engine, pathToMoxFile, textureManager);
                }
            }
            throw new Exception("Unsupported filetype");
        }

        /// <summary>
        /// Releasing all unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            if (this.instanceBuffer != null)
                this.instanceBuffer.Dispose();
            this.instanceBuffer = null;
        }

        /// <summary>
        /// Updates the instancing buffer with the courent stored data.
        /// </summary>
        public void UpdateBuffer()
        {
            Dispose();

            if (this.objectInstances.Count == 0)
                return;

            // Update bufferinformation
            this.vertexBufferDescription.SizeInBytes = this.objectInstances.Values.Count * Marshal.SizeOf(typeof(ObjectInstance));

            // Creating new buffer.
            // Locks the buffer, stored new data and releasing it.
            lock (this.engine.Device.ImmediateContext)
            {
                this.instanceBuffer = SharpDX.Direct3D11.Buffer.Create(this.engine.Device, this.objectInstances.Values.ToArray(), this.vertexBufferDescription);
                this.vertexBufferBinding = new VertexBufferBinding(this.instanceBuffer, Marshal.SizeOf(typeof(ObjectInstance)), 0);
            }
        }

        #endregion Public Methods

        #region Internal Methods

        internal void ToStream(BinaryWriter bw)
        {
            bw.Write("INS".ToCharArray());
            bw.Write((byte)1); // VersionNumber
            bw.Write(this.objectInstances.Count);
            foreach (var item in this.objectInstances)
            {
                // Write the Base Node properties
                item.Key.ToStream(bw);
                // Write the properties which are assigned to the BaseNode
                bw.Write((string)item.Key.Properties);
                // Write the object instance data.
                item.Value.ToStream(bw);
            }
        }

        #endregion Internal Methods

        #region Private Methods

        private static ObjectsProperties LoadV1(BinaryReader br, Engine engine, string pathToMoxFile, TextureManager textureManager)
        {
            int numberOfInstances = br.ReadInt32();
            ObjectsProperties properties = new ObjectsProperties(engine);
            properties.moxFile = MoxFile.FromFile(pathToMoxFile, engine, textureManager);

            for (int i = 0; i < numberOfInstances; i++)
            {
                BaseNode baseNode = BaseNode.FromStream(br, engine);
                baseNode.Properties = br.ReadString();

                ObjectInstance objInstance = ObjectInstance.FromStream(br);
                properties.objectInstances.Add(baseNode, objInstance);
            }
            properties.UpdateBuffer();
            return properties;
        }

        #endregion Private Methods
    }

    public class SceneryObjectManager : DynObjectManager, IRenderableObject, IDisposable
    {
        #region Public Fields

        public bool drawObjects = true;

        public string objectFolder;

        #endregion Public Fields

        #region Private Fields

        private BlendState blendStateLeaves;

        private int currentObjectId;

        private Engine engine;

        private SharpDX.Direct3D11.Buffer LeavePositionBuffer;

        private Dictionary<string, ObjectsProperties> objects;

        private SamplerState samplerForTrees;

        private ShaderBase shaderObjectInstance;

        private ShaderBase shaderTreeLeave;

        private TextureManager textureManager;

        private ObjectInstance treePositionData;

        private Dictionary<string, TreeProperties> trees;

        /// <summary>
        /// Vertex Elements for object instance rendering.
        /// </summary>
        private InputElement[] vertexElements = new InputElement[]
        {
            new InputElement("POSITION",0, SharpDX.DXGI.Format.R32G32B32_Float, 0,0),
            new InputElement("NORMAL",0, SharpDX.DXGI.Format.R32G32B32_Float, 12,0),
            new InputElement("TEXCOORD",0, SharpDX.DXGI.Format.R32G32_Float, 24,0),
            new InputElement("INSTANCE",1, SharpDX.DXGI.Format.R32G32B32A32_Float, 0,1, InputClassification.PerInstanceData,1), // Position
            new InputElement("INSTANCE",2, SharpDX.DXGI.Format.R32G32B32A32_Float, 16,1, InputClassification.PerInstanceData,1), // mx
            new InputElement("INSTANCE",3, SharpDX.DXGI.Format.R32G32B32A32_Float, 32,1, InputClassification.PerInstanceData,1), // my
            new InputElement("INSTANCE",4, SharpDX.DXGI.Format.R32G32B32A32_Float, 48,1, InputClassification.PerInstanceData,1), // mz
            new InputElement("INSTANCE",5, SharpDX.DXGI.Format.R32G32B32A32_Float, 64,1, InputClassification.PerInstanceData,1), // mw
            new InputElement("INSTANCE",6, SharpDX.DXGI.Format.R32_Float, 80,1, InputClassification.PerInstanceData,1) // scale
        };

        /// <summary>
        /// Vertex elements for tree leaves instance rendering.
        /// </summary>
        private InputElement[] vertexElementsTree = new InputElement[]
        {
            new InputElement("POSITION",0, SharpDX.DXGI.Format.R32G32B32_Float, 0,0),
            new InputElement("NORMAL",0, SharpDX.DXGI.Format.R32G32B32_Float, 12,0),
            new InputElement("TEXCOORD",0, SharpDX.DXGI.Format.R32G32_Float, 24,0),
            new InputElement("TEXCOORD",1, SharpDX.DXGI.Format.R32G32B32A32_Float, 0,1, InputClassification.PerInstanceData,1),
            new InputElement("COLOR",0, SharpDX.DXGI.Format.R32G32B32A32_Float, 16,1, InputClassification.PerInstanceData,1)
        };

        private float viewDistance = 6500.0f;

        #endregion Private Fields

        #region Public Constructors

        /// <summary>
        /// Creates a new Instance for scenery objects.
        /// It managed the rendering, loading and optimizing for the mox-Objects and trees.
        /// </summary>
        /// <param name="engine">Graphic Engine</param>
        /// <param name="numberOfSectorsX">not used atm</param>
        /// <param name="numberOfSectorsY">not used atm</param>
        public SceneryObjectManager(Engine engine, int numberOfSectorsX, int numberOfSectorsY, ShaderManager shaderManager)
            : base(engine)
        {
            this.engine = engine;
            // Adds this manager to the Rendermanger.
            this.engine.Grafic.AddRenderableObjects(this, 10);

            // Object folder where objects and tree objects are stored.
            this.objectFolder = Application.StartupPath + "\\MoxObjects\\";

            // Texturefolder where the textures of objects and trees should be.
            // This folder is used when the scenario editor uses this manger.
            string textureFolder = Application.StartupPath + "\\MoxTextures\\";

            // When the modelviewer starts by doubleclick a *.Mox-File. This folder is used.
            if (!Directory.Exists(textureFolder))
                textureFolder = "\\Textures\\";

            // TextureManager where all textures for the objects are managed.
            this.textureManager = new TextureManager(this.engine, textureFolder);

            // Objectinstance shader for rendering the mox objects.
            this.shaderObjectInstance = new ShaderBase(this.engine.Grafic.Device, shaderManager);
            this.shaderObjectInstance.LoadShader(this.engine.Device, ".\\Shaders\\ObjectInst.fx", "ps_main", "vs_main");
            this.shaderObjectInstance.inputLayout = new InputLayout(this.engine.Device, this.shaderObjectInstance.shaderByteCodeVS, this.vertexElements);

            // Treeleaves  shader for rendering the leaves of the trees.
            this.shaderTreeLeave = new ShaderBase(this.engine.Grafic.Device, shaderManager);
            this.shaderTreeLeave.LoadShader(this.engine.Device, ".\\Shaders\\TreeLeaves.fx", "ps_main", "vs_main");
            this.shaderTreeLeave.inputLayout = new InputLayout(this.engine.Grafic.Device, this.shaderTreeLeave.shaderByteCodeVS, this.vertexElementsTree);

            // Here are all known objects are stored in.
            this.objects = new Dictionary<string, ObjectsProperties>();
            // Here are all known trees are stored in.
            this.trees = new Dictionary<string, TreeProperties>();

            base.AddNode += new AddNodeHandler(SceneryObjectManager_AddNode);
            base.DeleteNode += new DeleteNodeHandler(SceneryObjectManager_DeleteNode);
            base.NodePropertiesChanged += new NodePropertiesHandler(SceneryObjectManager_NodePropertiesChanged);

            this.treePositionData = new ObjectInstance() { position = Vector4.Zero, rotatationY = Matrix.RotationY(0.5f), scale = 1.0f };
            // Create Constantbuffer for rendering treeleaves

            BufferDescription constanBufferDescription = new BufferDescription()
            {
                CpuAccessFlags = SharpDX.Direct3D11.CpuAccessFlags.None,
                Usage = ResourceUsage.Default,
                OptionFlags = ResourceOptionFlags.None,
                BindFlags = BindFlags.ConstantBuffer
            };
            constanBufferDescription.SizeInBytes = Marshal.SizeOf(this.treePositionData);
            this.LeavePositionBuffer = SharpDX.Direct3D11.Buffer.Create(this.engine.Grafic.Device, new[] { this.treePositionData }, constanBufferDescription);

            CreateBlendStateForLeaves();
            CreateSampleStates();
        }

        public void SaveToZip(ZipArchive sceneryZipFile)
        {
            using (MemoryStream ms = new MemoryStream())
            using (BinaryWriter bw = new BinaryWriter(ms))
            {
                this.ToStream(bw);
                bw.BaseStream.Position = 0;
                sceneryZipFile.AddData("DynObjectManager/ObjectManager.bin", bw.BaseStream, true);
            }
        }

        #endregion Public Constructors

        #region Private Destructors

        /// <summary>
        /// Deconstructor which automatically remove this from the render stack.
        /// </summary>
        ~SceneryObjectManager()
        {
            this.engine.Grafic.RemoveRenderableObject(this);
            base.Dispose();
            Dispose();
        }

        #endregion Private Destructors

        #region Public Delegates

        public delegate void LoadingStatusChanged(object sender, EventArgs e);

        #endregion Public Delegates

        #region Public Events

        public static event LoadingStatusChanged loadingStatusChanged;

        #endregion Public Events

        #region Public Properties

        public DepthStencilState depthWriteDisable { get; set; }

        public TextureManager TextureManager
        {
            get { return this.textureManager; }
            set { this.textureManager = value; }
        }

        #endregion Public Properties

        #region Public Methods

        public static SceneryObjectManager FromFile(string filename, Engine engine, string objectFolder)
        {
            FileStream fs = new FileStream(filename, FileMode.Open);
            BinaryReader br = new BinaryReader(fs);

            SceneryObjectManager manager = SceneryObjectManager.FromStream(br, engine, objectFolder);

            br.Close();
            fs.Close();

            return manager;
        }

        public static SceneryObjectManager FromStream(BinaryReader br, Engine engine, string objectFolder)
        {
            string ident = new string(br.ReadChars(3));
            if (ident == "OBJ")
            {
                switch (br.ReadByte()) // Version number
                {
                    case 1:
                        return LoadV1(br, engine, objectFolder);
                }
            }
            throw new Exception("Unsupported file format!");
        }

        public static SceneryObjectManager FromZipFile(ZipArchive sceneryZipFile, Engine engine, string objectFolder)
        {
            BinaryReader br = new BinaryReader(sceneryZipFile.GetStream("DynObjectManager/ObjectManager.bin"));

            return SceneryObjectManager.FromStream(br, engine, objectFolder);
        }

        /// <summary>
        /// Adds a mew Object to this manager.
        /// </summary>
        /// <param name="fileName">Filename with extension. </param>
        /// <param name="position">position where the object is set on terrain</param>
        /// <param name="angle">rotation of the object along the y-axe. value is in RADIANS</param>
        public void AddObject(string fileName, Vector3 position, float angle)
        {
            MessageBox.Show("Pleas do not use this anymore!!");
            string extension = Path.GetExtension(fileName).ToUpper();

            if (!File.Exists(this.objectFolder + fileName))
                return;

            if (extension != ".MOX" &&
                extension != ".TREE")
                return;

            switch (extension)
            {
                case ".MOX":
                    AddMoxObject(fileName, position, angle);
                    break;

                case ".TREE":
                    AddTreeObject(fileName, position, angle);
                    break;
            }
        }

        // Public Methods (24) 
        public void AddObject(BaseNode baseNode, float angle, float scale)
        {
            string extension = Path.GetExtension((string)baseNode.Properties).ToUpper();

            if (!File.Exists(this.objectFolder + (string)baseNode.Properties))
                throw new FileNotFoundException("File Not found: " + (string)baseNode.Properties);

            if (extension != ".MOX" &&
                extension != ".TREE")
                return;

            switch (extension)
            {
                case ".MOX":
                    AddMoxObject(baseNode, angle, scale);
                    break;

                case ".TREE":
                    AddTreeObject(baseNode, angle, scale);
                    break;
            }
        }

        public void CopyObjectsToFolder(string scenarioFoldername)
        {
            List<string> fileNames = new List<string>();
            List<int> textureIds = new List<int>();
            List<string> textureNames = new List<string>();
            // object file names
            foreach (var item in this.objects)
            {
                fileNames.Add(item.Key);
            }
            foreach (var item in this.trees)
            {
                fileNames.Add(item.Key);
            }
            // Texturenames;
            foreach (var item in this.objects.Values)
            {
                foreach (var id in item.moxFile.TextureIds)
                {
                    if (!textureIds.Contains(id))
                        textureIds.Add(id);
                }
            }
            foreach (var item in this.trees.Values)
            {
                foreach (var id in item.treeFile.TextureIds)
                {
                    if (!textureIds.Contains(id))
                        textureIds.Add(id);
                }
            }

            foreach (var item in textureIds)
            {
                string textureName = this.textureManager.GetTextureName(item);
                if (!textureNames.Contains(textureName))
                    textureNames.Add(textureName);
            }

            if (!Directory.Exists(scenarioFoldername + "\\V1\\Objects\\"))
                Directory.CreateDirectory(scenarioFoldername + "\\V1\\Objects\\");
            if (!Directory.Exists(scenarioFoldername + "\\V1\\Objects\\Textures\\"))
                Directory.CreateDirectory(scenarioFoldername + "\\V1\\Objects\\Textures\\");

            foreach (var filenme in fileNames)
            {
                if (!File.Exists(scenarioFoldername + "\\V1\\Objects\\" + Path.GetFileName(filenme)))
                    if (File.Exists(this.objectFolder + filenme))
                        File.Copy(this.objectFolder + filenme, scenarioFoldername + "\\V1\\Objects\\" + Path.GetFileName(filenme));

                string mtlFile = filenme.Substring(0, filenme.Length - 4) + ".mtl";
                if (!File.Exists(scenarioFoldername + "\\V1\\Objects\\" + Path.GetFileName(mtlFile)))
                    if (File.Exists(this.objectFolder + mtlFile))
                        File.Copy(this.objectFolder + mtlFile, scenarioFoldername + "\\V1\\Objects\\" + Path.GetFileName(mtlFile));
            }
            foreach (var fileName in textureNames)
            {
                if (!File.Exists(scenarioFoldername + "\\V1\\Objects\\Textures\\" + Path.GetFileName(fileName)))
                    if (File.Exists((this.textureManager.TextureFolder + fileName)))
                        File.Copy(this.textureManager.TextureFolder + fileName, scenarioFoldername + "\\V1\\Objects\\Textures\\" + Path.GetFileName(fileName));
            }
        }

        public void CycleDrawMode()
        {
            this.drawObjects = !this.drawObjects;
        }

        public void DeleteObject(string fileName, BaseNode baseNode)
        {
            ObjectsProperties properties;
            TreeProperties treeProperties;
            if (this.objects.TryGetValue(fileName, out properties))
            {
                if (properties.objectInstances.Remove(baseNode))
                {
                    //if (properties.objectInstances.Count > 0)
                    properties.UpdateBuffer();
                    //else
                    //    this.objects.Remove(fileName);
                    return;
                }
            }
            else if (this.trees.TryGetValue(fileName, out treeProperties))
            {
                if (treeProperties.objectInstances.Remove(baseNode))
                {
                    //if (treeProperties.objectInstances.Count > 0)
                    treeProperties.UpdateBuffer();
                    //else
                    //    this.trees.Remove(fileName);
                    return;
                }
            }
            MessageBox.Show(fileName + " wurde nicht gelöscht!");
        }

        /// <summary>
        /// Releasing all unmanged resources
        /// </summary>
        public new void Dispose()
        {
            // Remove from rendering
            this.engine.Grafic.RemoveRenderableObject(this);

            base.Dispose();
            // Shader effects
            if (this.shaderObjectInstance != null)
                this.shaderObjectInstance.Dispose();
            this.shaderObjectInstance = null;
            if (this.shaderTreeLeave != null)
                this.shaderTreeLeave.Dispose();
            this.shaderTreeLeave = null;

            // All objects
            if (this.objects != null)
                foreach (var item in this.objects)
                {
                    item.Value.Dispose();
                }
            // All trees
            if (this.trees != null)
                foreach (var item in this.trees)
                {
                    item.Value.Dispose();
                }
        }

        public string[] ExportGetAllObjectNames()
        {
            List<string> objectNames = new List<string>();

            foreach (var item in this.objects)
            {
                string objectName = Path.GetFileName(item.Key);
                if (!objectNames.Contains(objectName))
                    objectNames.Add(objectName);
            }
            foreach (var item in this.trees)
            {
                string objectName = "T\\" + Path.GetFileName(item.Key);
                if (!objectNames.Contains(objectName))
                    objectNames.Add(objectName);
            }

            return objectNames.ToArray();
        }

        public BaseObject[] ExportGetAllObjects()
        {
            return ExportGetAllObjects(true);
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="onlyFilename"></param>
        /// <returns></returns>
        public BaseObject[] ExportGetAllObjects(bool onlyFilename)
        {
            List<BaseObject> baseObjects = new List<BaseObject>();

            foreach (var item in this.objects)
            {
                ObjectsProperties properties = item.Value;
                foreach (var instance in properties.objectInstances)
                {
                    ObjectInstance inst = instance.Value;

                    string name = (onlyFilename) ? Path.GetFileName(item.Key) : item.Key;
                    double angle = -GeoMath.ConvertRadianToDegree(Math.Atan2(inst.rotatationY.M13, inst.rotatationY.M33));

                    while (angle < 0.0) angle += 360.0;
                    while (angle >= 360.0) angle -= 360.0;

                    BaseObject obj = new BaseObject(
                        name,
                        inst.position.X,
                        inst.position.Y,
                        inst.position.Z,
                        (float)angle,
                        inst.scale);

                    baseObjects.Add(obj);
                }
            }

            foreach (var item in this.trees)
            {
                TreeProperties properties = item.Value;
                foreach (var instance in properties.objectInstances)
                {
                    ObjectInstance inst = instance.Value;
                    string name = (onlyFilename) ? Path.GetFileName(item.Key) : item.Key;
                    BaseObject obj = new BaseObject(name, inst.position.X, inst.position.Y, inst.position.Z, (float)(Math.Acos(inst.rotatationY.M11) / Math.PI * 180.0), inst.scale);
                    baseObjects.Add(obj);
                }
            }
            return baseObjects.ToArray();
        }

        public BaseObject GetBaseObject(BaseNode baseNode)
        {
            string fileName = (string)baseNode.Properties;
            ObjectsProperties properties;
            TreeProperties treeProperties;
            ObjectInstance instance;
            if (this.objects.TryGetValue(fileName, out properties))
            {
                if (properties.objectInstances.TryGetValue(baseNode, out instance))
                {
                    return new BaseObject()
                    {
                        AngleY = -(float)Math.Atan2(instance.rotatationY.M13, instance.rotatationY.M33),
                        Name = fileName,
                        PositionX = instance.position.X,
                        PositionY = instance.position.Y,
                        PositionZ = instance.position.Z,
                        Size = instance.scale
                    };
                }
            }
            else if (this.trees.TryGetValue(fileName, out treeProperties))
            {
                if (treeProperties.objectInstances.TryGetValue(baseNode, out instance))
                {
                    return new BaseObject()
                    {
                        AngleY = -(float)Math.Atan2(instance.rotatationY.M13, instance.rotatationY.M33),
                        Name = fileName,
                        PositionX = instance.position.X,
                        PositionY = instance.position.Y,
                        PositionZ = instance.position.Z,
                        Size = instance.scale
                    };
                }
            }
            throw new Exception("No Properties found for this node!");
        }

        public void GetProperties(BaseNode baseNode, out Vector3 position, out float angle, out float scale)
        {
            string fileName = (string)baseNode.Properties;
            ObjectsProperties properties;
            TreeProperties treeProperties;
            ObjectInstance instance;
            if (this.objects.TryGetValue(fileName, out properties))
            {
                if (properties.objectInstances.TryGetValue(baseNode, out instance))
                {
                    position = new Vector3(instance.position.X, instance.position.Y, instance.position.Z);
                    angle = -(float)Math.Atan2(instance.rotatationY.M13, instance.rotatationY.M33);
                    scale = instance.scale;
                    return;
                }
            }
            else if (this.trees.TryGetValue(fileName, out treeProperties))
            {
                if (treeProperties.objectInstances.TryGetValue(baseNode, out instance))
                {
                    position = new Vector3(instance.position.X, instance.position.Y, instance.position.Z);
                    angle = -(float)Math.Atan2(instance.rotatationY.M13, instance.rotatationY.M33);
                    scale = instance.scale;
                    return;
                }
            }
            throw new Exception("No Properties found for this node!");
        }

        public MyOwnVertex.PositionNormalTextured[] GetVerticesFromObject(BaseNode baseNode)
        {
            string extension = Path.GetExtension((string)baseNode.Properties).ToUpper();

            if (!File.Exists(this.objectFolder + (string)baseNode.Properties))
                throw new Exception("Filename not found: " + (string)baseNode.Properties);

            if (extension != ".MOX" &&
                extension != ".TREE")
                return null;

            switch (extension)
            {
                case ".MOX":
                    return GetVerticesFromMoxObject(baseNode);

                case ".TREE":
                    return GetVerticesFromTreeObject(baseNode);
            }
            throw new Exception("Can´t get vertices from: " + (string)baseNode.Properties);
        }

        public new void LockAllObjects()
        {
            base.LockAllObjects();
        }

        public new void Render(int renderStep)
        {
            if (!this.drawObjects)
                return;

            this.engine.Grafic.SetFillmodeSolid();
            this.engine.Grafic.DepthStateEnableZ(true);

            RenderMoxObjectsInstancing();
            RenderTreeObjectsInstancing();

            base.Render(renderStep); // nodes...: Bounding box
        }

        public void SetViewDistance(float newDistance)
        {
            this.viewDistance = newDistance * 10.0f;
        }

        public void ToFile(string Filenam)
        {
            FileStream fs = new FileStream(Filenam, FileMode.Create);
            BinaryWriter bw = new BinaryWriter(fs);

            ToStream(bw);

            bw.Close();
            fs.Close();
        }

        public void ToStream(BinaryWriter bw)
        {
            // Delete unused objects.
            List<string> objectsTobeDeleted = new List<string>();
            foreach (var item in this.objects)
            {
                if (item.Value.objectInstances.Count == 0)
                    objectsTobeDeleted.Add(item.Key);
            }
            foreach (var key in objectsTobeDeleted)
            {
                this.objects.Remove(key);
            }
            objectsTobeDeleted.Clear();
            foreach (var item in this.trees)
            {
                if (item.Value.objectInstances.Count == 0)
                    objectsTobeDeleted.Add(item.Key);
            }
            foreach (var key in objectsTobeDeleted)
            {
                this.trees.Remove(key);
            }

            bw.Write("OBJ".ToCharArray());
            bw.Write((byte)1); // Version number.
            bw.Write((short)this.objects.Values.Count);
            bw.Write((short)this.trees.Values.Count);

            foreach (var item in this.objects)
            {
                // filename
                bw.Write((string)item.Key);
                // ObjectProperties
                item.Value.ToStream(bw);
            }
            foreach (var item in this.trees)
            {
                bw.Write((string)item.Key);
                item.Value.ToStream(bw);
            }
        }

        public new void UnlockAllObjects()
        {
            base.UnlockAllObjects();
        }

        public void UpdateBoundingBox(BaseNode baseNode, float angle, float scale)
        {
            MyOwnVertex.PositionNormalTextured[] vertices = GetVerticesFromObject(baseNode);
            Vector3[] positions = new Vector3[vertices.Length];

            for (int i = 0; i < vertices.Length; i++)
            {
                positions[i] = vertices[i].Position * 0.5f;
                positions[i].Y += baseNode.HeightOverTerrain / scale;
            }

            baseNode.BoundingBox.Update(positions, baseNode.Position, angle, scale);
        }

        #endregion Public Methods

        #region Private Methods

        private static SceneryObjectManager LoadV1(BinaryReader br, Engine engine, string objectFolder)
        {
            StringBuilder sb = new StringBuilder("Can´t find the following objects: \n");
            bool missingTextures = false;
            SceneryObjectManager sceneryObjectManager = new SceneryObjectManager(engine, 0, 0, engine.Grafic.ShaderManager);
            sceneryObjectManager.objectFolder = objectFolder;

            int numberOfObjects = br.ReadInt16();
            int numberOfTrees = br.ReadInt16();

            for (int i = 0; i < numberOfObjects; i++)
            {
                string filename = br.ReadString();

                ObjectsProperties prop = ObjectsProperties.FromStream(br, engine, objectFolder + filename, sceneryObjectManager.TextureManager);

                if (File.Exists(objectFolder + filename))
                {
                    sceneryObjectManager.objects.Add(filename, prop);
                }
                else
                {
                    sb.AppendLine(objectFolder + filename);
                    missingTextures = true;
                    prop.Dispose();
                }

                SendLoadingStatus(string.Format("Loading Objects: {0}/{1}", i, numberOfObjects), numberOfObjects, i);
            }

            for (int i = 0; i < numberOfTrees; i++)
            {
                string filename = br.ReadString();

                TreeProperties prop = TreeProperties.FromStream(br, engine, objectFolder + filename, sceneryObjectManager.TextureManager);
                if (File.Exists(objectFolder + filename))
                {
                    sceneryObjectManager.trees.Add(filename, prop);
                }
                else
                {
                    sb.AppendLine(objectFolder + filename);
                    missingTextures = true;
                    prop.Dispose();
                }
            }

            foreach (var obj in sceneryObjectManager.objects.Values)
            {
                foreach (var ins in obj.objectInstances)
                {
                    ins.Key.Locked = true;
                    sceneryObjectManager.Nodes.Add(ins.Key);
                    float angle = (float)Math.Atan2(ins.Value.rotatationY.M13, ins.Value.rotatationY.M33);
                    sceneryObjectManager.UpdateBoundingBox(ins.Key, angle, ins.Value.scale);
                }
            }
            foreach (var obj in sceneryObjectManager.trees.Values)
            {
                foreach (var ins in obj.objectInstances)
                {
                    ins.Key.Locked = true;
                    sceneryObjectManager.Nodes.Add(ins.Key);
                    float angle = (float)Math.Atan2(ins.Value.rotatationY.M13, ins.Value.rotatationY.M33);
                    sceneryObjectManager.UpdateBoundingBox(ins.Key, angle, ins.Value.scale);
                }
            }

            if (missingTextures)
                MessageBox.Show(sb.ToString());

            return sceneryObjectManager;
        }

        private static void SendLoadingStatus(string Text, int numberOfItems, int currentItem)
        {
            if (loadingStatusChanged != null)
                loadingStatusChanged(new LoadingStatus()
                {
                    Text = Text,
                    CurrentItem = currentItem,
                    NumberOfItems = numberOfItems
                }, EventArgs.Empty);
        }

        private void AddMoxObject(string fileName, Vector3 position, float angle)
        {
            ObjectsProperties objProperties;
            if (this.objects.TryGetValue(fileName, out objProperties))
            {
                BaseNode baseNode = new BaseNode(this.engine, position);
                objProperties.objectInstances.Add(baseNode, new ObjectInstance()
                {
                    position = new Vector4(position, 1.0f),
                    rotatationY = Matrix.RotationY(angle)
                }
                );
                baseNode.Properties = fileName;
                this.Nodes.Add(baseNode);
            }
            else
            {
                objProperties = new ObjectsProperties(this.engine)
                {
                    moxFile = MoxFile.FromFile(this.objectFolder + fileName, this.engine, this.textureManager),
                    objectInstances = new Dictionary<BaseNode, ObjectInstance>()
                };

                BaseNode baseNode = new BaseNode(this.engine, position);
                objProperties.objectInstances.Add(baseNode,
                    new ObjectInstance()
                    {
                        position = new Vector4(position, 1.0f),
                        rotatationY = Matrix.RotationY(angle)
                    }
                    );
                this.objects.Add(fileName, objProperties);
                baseNode.Properties = fileName;
                this.Nodes.Add(baseNode);
            }
            objProperties.UpdateBuffer();
        }

        // Private Methods (15) 
        private int AddMoxObject(BaseNode baseNode, float angle, float scale)
        {
            int indexInList = GetNewObjectId();
            ObjectsProperties objProperties;
            if (this.objects.TryGetValue((string)baseNode.Properties, out objProperties))
            {
                Vector3 position = baseNode.Position;
                position.Y += baseNode.HeightOverTerrain;

                objProperties.objectInstances.Add(baseNode, new ObjectInstance()
                {
                    position = new Vector4(position, 1.0f),
                    rotatationY = Matrix.RotationY(angle),
                    scale = scale
                }
                );
            }
            else
            {
                objProperties = new ObjectsProperties(this.engine)
                {
                    moxFile = MoxFile.FromFile(this.objectFolder + (string)baseNode.Properties, this.engine, this.textureManager),
                    objectInstances = new Dictionary<BaseNode, ObjectInstance>()
                };

                Vector3 position = baseNode.Position;
                position.Y += baseNode.HeightOverTerrain;

                objProperties.objectInstances.Add(baseNode,
                    new ObjectInstance()
                    {
                        position = new Vector4(position, 1.0f),
                        rotatationY = Matrix.RotationY(angle),
                        scale = scale
                    }
                );
                this.objects.Add((string)baseNode.Properties, objProperties);
            }
            objProperties.UpdateBuffer();
            return indexInList;
        }

        private void AddTreeObject(BaseNode baseNode, float angle, float scale)
        {
            TreeProperties objProperties;
            if (this.trees.TryGetValue((string)baseNode.Properties, out objProperties))
            {
                Vector3 position = baseNode.Position;
                position.Y += baseNode.HeightOverTerrain;

                objProperties.objectInstances.Add(baseNode, new ObjectInstance()
                {
                    position = new Vector4(position, 1.0f),
                    rotatationY = Matrix.RotationY(angle),
                    scale = scale
                }
                );
                baseNode.Properties = (string)baseNode.Properties;
            }
            else
            {
                objProperties = new TreeProperties(this.engine)
                {
                    treeFile = TreeFile.FromFile(this.engine, this.objectFolder + (string)baseNode.Properties, this.textureManager),
                    objectInstances = new Dictionary<BaseNode, ObjectInstance>()
                };

                Vector3 position = baseNode.Position;
                position.Y += baseNode.HeightOverTerrain;

                objProperties.objectInstances.Add(baseNode,
                    new ObjectInstance()
                    {
                        position = new Vector4(position, 1.0f),
                        rotatationY = Matrix.RotationY(angle),
                        scale = scale
                    }
                    );
                this.trees.Add((string)baseNode.Properties, objProperties);
            }
            objProperties.UpdateBuffer();
        }

        private void AddTreeObject(string fileName, Vector3 position, float angle)
        {
            TreeProperties objProperties;
            if (this.trees.TryGetValue(fileName, out objProperties))
            {
                BaseNode baseNode = new BaseNode(this.engine, position);

                objProperties.objectInstances.Add(baseNode, new ObjectInstance()
                {
                    position = new Vector4(position, 1.0f),
                    rotatationY = Matrix.RotationY(angle)
                }
                );
                baseNode.Properties = fileName;
                this.Nodes.Add(baseNode);
            }
            else
            {
                objProperties = new TreeProperties(this.engine)
                {
                    treeFile = TreeFile.FromFile(this.engine, this.objectFolder + fileName, this.textureManager),
                    objectInstances = new Dictionary<BaseNode, ObjectInstance>()
                };

                BaseNode baseNode = new BaseNode(this.engine, position);
                objProperties.objectInstances.Add(baseNode,
                    new ObjectInstance()
                    {
                        position = new Vector4(position, 1.0f),
                        rotatationY = Matrix.RotationY(-angle)
                    }
                    );
                this.trees.Add(fileName, objProperties);
                baseNode.Properties = fileName;
                this.Nodes.Add(baseNode);
            }
            objProperties.UpdateBuffer();
        }

        private void CreateBlendStateForLeaves()
        {
            BlendStateDescription blendStateDescription = new BlendStateDescription();
            blendStateDescription.AlphaToCoverageEnable = false;
            blendStateDescription.IndependentBlendEnable = false;
            blendStateDescription.RenderTarget[0].AlphaBlendOperation = BlendOperation.Add;
            blendStateDescription.RenderTarget[0].DestinationAlphaBlend = BlendOption.Zero;
            blendStateDescription.RenderTarget[0].BlendOperation = BlendOperation.Add;
            blendStateDescription.RenderTarget[0].DestinationBlend = BlendOption.InverseSourceAlpha;
            blendStateDescription.RenderTarget[0].IsBlendEnabled = true;
            blendStateDescription.RenderTarget[0].RenderTargetWriteMask = ColorWriteMaskFlags.All;
            blendStateDescription.RenderTarget[0].SourceAlphaBlend = BlendOption.One;
            blendStateDescription.RenderTarget[0].SourceBlend = BlendOption.SourceAlpha;

            this.blendStateLeaves = new BlendState(this.engine.Grafic.Device, blendStateDescription);
        }

        private void CreateDepthStencilState()
        {
            if (this.depthWriteDisable != null)
                return;

            DepthStencilStateDescription depthWriteDescription = new DepthStencilStateDescription()
            {
                DepthWriteMask = DepthWriteMask.Zero,
                DepthComparison = Comparison.LessEqual,
                IsDepthEnabled = true,
                IsStencilEnabled = false,
                StencilReadMask = 0xFF,
                StencilWriteMask = 0xFF,

                FrontFace = new DepthStencilOperationDescription()
                {
                    FailOperation = StencilOperation.Keep,
                    DepthFailOperation = StencilOperation.Increment,
                    PassOperation = StencilOperation.Keep,
                    Comparison = Comparison.Always,
                },

                BackFace = new DepthStencilOperationDescription()
                {
                    FailOperation = StencilOperation.Keep,
                    DepthFailOperation = StencilOperation.Decrement,
                    PassOperation = StencilOperation.Keep,
                    Comparison = Comparison.Always
                }
            };
            //this.engine.Grafic.SetWireframe();
            lock (this.engine.Grafic.Device)
                depthWriteDisable = new DepthStencilState(this.engine.Grafic.Device, depthWriteDescription);
        }

        private void CreateSampleStates()
        {
            // Samplerstate for Texture 1
            SamplerStateDescription samplerStateDescription = new SamplerStateDescription()
            {
                AddressU = TextureAddressMode.Wrap,
                AddressV = TextureAddressMode.Wrap,
                AddressW = TextureAddressMode.Wrap,
                BorderColor = new Color4(0),
                ComparisonFunction = Comparison.Always,
                Filter = Filter.MinMagLinearMipPoint,
                MaximumAnisotropy = 16,
                MaximumLod = float.MaxValue,
                MinimumLod = 0,
                MipLodBias = 0
            };
            this.samplerForTrees = new SamplerState(this.engine.Device, samplerStateDescription);
        }

        private int GetNewObjectId()
        {
            return ++this.currentObjectId;
        }

        private MyOwnVertex.PositionNormalTextured[] GetVerticesFromMoxObject(BaseNode baseNode)
        {
            ObjectsProperties property;
            if (this.objects.TryGetValue((string)baseNode.Properties, out property))
                return property.moxFile.Vertices;
            throw new Exception("Couldn´t find mox file in array: \n" + (string)baseNode.Properties);
        }

        private MyOwnVertex.PositionNormalTextured[] GetVerticesFromTreeObject(BaseNode baseNode)
        {
            TreeProperties property;
            if (this.trees.TryGetValue((string)baseNode.Properties, out property))
                return property.treeFile.Vertices;
            throw new Exception("Couldn´t find mox file in array: \n" + (string)baseNode.Properties);
        }

        private void PerformanceTest()
        {
            string filename = "test.mox";
            this.objects.Add(filename, new ObjectsProperties(this.engine)
            {
                moxFile = MoxFile.FromFile(filename, this.engine, this.textureManager),
                objectInstances = new Dictionary<BaseNode, ObjectInstance>()
            });

            ObjectsProperties objInstance;
            int indexInList = GetNewObjectId();
            if (this.objects.TryGetValue(filename, out objInstance))
            {
                for (int x = 0; x < 22; x++)
                {
                    for (int y = 0; y < 22; y++)
                    {
                        for (int z = 0; z < 22; z++)
                        {
                            Vector3 position = new Vector3(x * 200, z * 100, y * 200);
                            BaseNode baseNode = new BaseNode(this.engine, position);
                            objInstance.objectInstances.Add(baseNode,
                                new ObjectInstance()
                                {
                                    position = new Vector4(position, 1),
                                    rotatationY = Matrix.RotationY((float)(Math.PI / (x + 1)))
                                }
                                );
                        }
                    }
                }
                objInstance.UpdateBuffer();
            }
        }

        private void RenderMoxObjects()
        {
            RenderTargetBlendDescription renderTargetBlendDescription = new RenderTargetBlendDescription()
            {
                AlphaBlendOperation = BlendOperation.Add,
                BlendOperation = BlendOperation.Add,
                DestinationAlphaBlend = BlendOption.Zero,
                DestinationBlend = BlendOption.InverseSourceAlpha,
                IsBlendEnabled = true,
                RenderTargetWriteMask = ColorWriteMaskFlags.All,
                SourceAlphaBlend = BlendOption.Zero,
                SourceBlend = BlendOption.SourceAlpha
            };
            SharpDX.Direct3D11.BlendStateDescription blendStateDescription = new BlendStateDescription()
            {
                AlphaToCoverageEnable = false,
                IndependentBlendEnable = true
            };

            SharpDX.Direct3D11.BlendState blendState = new BlendState(this.engine.Device, blendStateDescription);

            this.engine.Device.ImmediateContext.OutputMerger.SetBlendState(blendState);

            //this.engine.Device.SetRenderState(RenderState.AlphaTestEnable, false);
            //this.engine.Device.SetRenderState(RenderState.AlphaBlendEnable, true);
            //this.engine.Device.SetRenderState(RenderState.BlendOperationAlpha, BlendOperation.Add);
            //this.engine.Device.SetRenderState(RenderState.SourceBlendAlpha, Blend.Zero);
            //this.engine.Device.SetRenderState(RenderState.DestinationBlendAlpha, Blend.Zero);
            //this.engine.Device.SetRenderState(RenderState.BlendOperation, BlendOperation.Add);
            //this.engine.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
            //this.engine.Device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha);
            //this.engine.Device.SetRenderState(RenderState.ZWriteEnable, true);

            //EffectHandle eh = this.effect.GetParameter(null, "matWorldViewProjection");
            //this.effect.SetValue(eh, this.engine.Grafic.WorldViewProjection);

            foreach (var item in this.objects)
            {
                var objects = item.Value;

                // Sets the Vertexbuffer where 3d vertices are stored in.
                this.engine.Device.ImmediateContext.InputAssembler.SetVertexBuffers(0, objects.moxFile.vertexBufferBinding);
                // Set the indices-buffer for the polygons
                this.engine.Device.ImmediateContext.InputAssembler.SetIndexBuffer(objects.moxFile.IndexBuffer, SharpDX.DXGI.Format.R16_UInt, 0);
                // Set the View Projection matrix for the shader

                // Render each material seperat to change the texture in the shader.
                for (int j = 0; j < objects.moxFile.AssignedMaterialIdForChunk.Length; j++)
                {
                    // Sets the texture for the 3d mesh-subset.
                    ShaderResourceView texture = this.textureManager.Textures[objects.moxFile.TextureIds[j]];

                    // d3d9 this.effect.SetTexture("base_Tex", texture);
                    // d3d9 this.effect.SetValue("color", objects.moxFile.Materials[objects.moxFile.AssignedMaterialIdForChunk[j]].DiffusColor);
                    this.engine.Device.ImmediateContext.PixelShader.SetShaderResource(0, texture);

                    //if (texture == null)
                    //    this.effect.SetValue("withoutTexture", true);
                    //else
                    //    this.effect.SetValue("withoutTexture", false);

                    // Render with the shader
                    foreach (var inst in item.Value.objectInstances.Values)
                    {
                        throw new NotImplementedException("alt. sollte alles über instanceing gehen...");
                        //this.effect.SetValue("position", inst.position);
                        //this.effect.SetValue("scale", inst.scale);
                        //this.effect.SetValue("rotationMatrix", inst.rotatationY);

                        //int passes = this.effect.Begin();
                        //for (int i = 0; i < passes; i++)
                        //{
                        //    this.effect.BeginPass(i);
                        //    objects.moxFile.RenderInstancing(j);
                        //    this.effect.EndPass();
                        //}
                        //this.effect.End();
                    }
                }
            }
        }

        private void RenderMoxObjectsInstancing()
        {
            lock (this.engine.Grafic.Device.ImmediateContext)
            {
                this.engine.Device.ImmediateContext.InputAssembler.PrimitiveTopology = SharpDX.Direct3D.PrimitiveTopology.TriangleList;

                // Renders all objects
                foreach (var item in this.objects)
                {
                    var objects = item.Value;

                    if (objects.objectInstances.Count == 0)
                        continue;

                    // Sets the Vertexbuffer where 3d vertices are stored in.
                    this.engine.Device.ImmediateContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding[]
                    {
                        objects.moxFile.vertexBufferBinding,
                        objects.vertexBufferBinding
                    });

                    // Set the indices-buffer for the polygons
                    this.engine.Device.ImmediateContext.InputAssembler.SetIndexBuffer(objects.moxFile.IndexBuffer, SharpDX.DXGI.Format.R16_UInt, 0);

                    // Render each material seperat to change the texture in the shader.
                    this.engine.Grafic.ShaderManager.SetShader(this.shaderObjectInstance);

                    this.engine.Device.ImmediateContext.PixelShader.SetSampler(0, this.samplerForTrees);

                    for (int j = 0; j < objects.moxFile.AssignedMaterialIdForChunk.Length; j++)
                    {
                        // Sets the texture for the 3d mesh-subset.
                        var texture = this.textureManager.Textures[objects.moxFile.TextureIds[j]];
                        if (texture != null)
                            //  this.engine.Device.ImmediateContext.PixelShader.SetShaderResource(0, this.textureManager.WhiteShaderResource);
                            ShaderBase.SetTexture(0, texture, this.engine.Grafic.Device);// this.shaderObjectInstance.SetTexture(0, texture);
                        else
                            ShaderBase.SetTexture(0, this.textureManager.WhiteShaderResource, this.engine.Grafic.Device);// this.shaderObjectInstance.SetTexture(0, this.textureManager.WhiteShaderResource);

                        // this.engine.Device.ImmediateContext.PixelShader.SetShaderResource(0, this.textureManager.WhiteShaderResource);

                        objects.moxFile.RenderInstancing(j, objects.objectInstances.Values.Count);
                    }
                }
            }
        }

        private void RenderTreeLeaves()
        {
            CreateDepthStencilState();
            lock (this.engine.Grafic.Device.ImmediateContext)
            {
                this.engine.Grafic.Device.ImmediateContext.OutputMerger.SetBlendState(this.blendStateLeaves);
                this.engine.Grafic.Device.ImmediateContext.OutputMerger.SetDepthStencilState(this.depthWriteDisable);

                this.engine.Grafic.ShaderManager.SetShader(this.shaderTreeLeave);

                foreach (var item in this.trees)
                {
                    var objects = item.Value;

                    if (objects.objectInstances.Count == 0)
                        continue;

                    // Set Vertexbuffer
                    this.engine.Device.ImmediateContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding[]{
                    objects.treeFile.vertexBufferBinding,
                    objects.treeFile.vertexBufferBindingLeaves
                });
                    ShaderBase.SetTexture(0, this.textureManager.Textures[objects.treeFile.TextureIds[1]], this.engine.Grafic.Device);

                    // Set Indexbuffer
                    this.engine.Device.ImmediateContext.InputAssembler.SetIndexBuffer(objects.treeFile.IndexBuffer, SharpDX.DXGI.Format.R16_UInt, 0);

                    int size = Marshal.SizeOf(typeof(TreeFile.LeavePosition));

                    foreach (var leaves in objects.objectInstances)
                    {
                        Vector3 treePosition = new Vector3(leaves.Value.position.X,
                                                            leaves.Value.position.Y,
                                                            leaves.Value.position.Z);

                        if (GeoMath.DistanceBtw2Points(treePosition, this.engine.Camera.CameraTartget) < this.viewDistance * 0.50f)
                        {
                            this.treePositionData = leaves.Value;
                            this.engine.Grafic.Device.ImmediateContext.UpdateSubresource(ref this.treePositionData, this.LeavePositionBuffer);
                            this.engine.Grafic.Device.ImmediateContext.VertexShader.SetConstantBuffer(13, this.LeavePositionBuffer);

                            // Render Leaves
                            objects.treeFile.RenderLeaves(objects.treeFile.LeavePositions.Length);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Renders the Trees.
        /// </summary>
        private void RenderTreeObjectsInstancing()
        {
            this.engine.Grafic.ShaderManager.SetShader(this.shaderObjectInstance);
            // Renders the trunks of the tree.
            foreach (var item in this.trees)
            {
                var objects = item.Value;

                if (objects.objectInstances.Count == 0)
                    continue;

                lock (this.engine.Grafic.Device.ImmediateContext)
                {
                    this.engine.Device.ImmediateContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding[]
                    {
                        objects.treeFile.vertexBufferBinding,
                        objects.vertexBufferBindingInstacing
                    });

                    this.engine.Device.ImmediateContext.InputAssembler.SetIndexBuffer(objects.treeFile.IndexBuffer, SharpDX.DXGI.Format.R16_UInt, 0);

                    // Set trunk texture.
                    this.engine.Device.ImmediateContext.PixelShader.SetShaderResource(0, this.textureManager.Textures[objects.treeFile.TextureIds[0]]);
                    objects.treeFile.RenderTrunksInstanced(objects.objectInstances.Count);
                }
            }
            RenderTreeLeaves();
        }

        private void SceneryObjectManager_AddNode(BaseNode baseNode, EventArgs e)
        {
            //AddNewNode(baseNode);
        }

        private void SceneryObjectManager_DeleteNode(BaseNode baseNode, EventArgs e)
        {
            //MessageBox.Show("DeletNode");
        }

        private void SceneryObjectManager_NodePropertiesChanged(BaseNode baseNode, EventArgs e)
        {
            //MessageBox.Show("MoveAddNode");
        }

        #endregion Private Methods

        #region Public Structs

        public struct LoadingStatus
        {
            #region Public Fields

            public int CurrentItem;
            public int NumberOfItems;
            public string Text;

            #endregion Public Fields
        }

        #endregion Public Structs
    }

    public class TreeProperties : IDisposable
    {
        #region Public Fields

        public Engine engine;
        public SharpDX.Direct3D11.Buffer instanceBuffer;
        public Dictionary<BaseNode, ObjectInstance> objectInstances;
        public TreeFile treeFile;

        #endregion Public Fields

        #region Private Fields

        private BufferDescription vertexBufferDescription;

        #endregion Private Fields

        #region Public Constructors

        public TreeProperties(Engine engine)
        {
            this.objectInstances = new Dictionary<BaseNode, ObjectInstance>();
            this.engine = engine;

            this.vertexBufferDescription = new BufferDescription()
            {
                BindFlags = BindFlags.VertexBuffer,
                CpuAccessFlags = 0,
                // OptionFlags = ResourceOptionFlags.BufferStructured,
                SizeInBytes = 0,
                Usage = ResourceUsage.Default
            };
        }

        #endregion Public Constructors

        #region Public Properties

        public VertexBufferBinding vertexBufferBindingInstacing { get; set; }

        #endregion Public Properties

        #region Public Methods

        public static TreeProperties FromStream(BinaryReader br, Engine engine, string pathToTreeFile, TextureManager textureManager)
        {
            string ident = new string(br.ReadChars(3));
            if (ident == "INS")
            {
                switch (br.ReadByte())
                {
                    case 1:
                        return LoadV1(br, engine, pathToTreeFile, textureManager);
                }
            }
            throw new Exception("Unsupported filetype");
        }

        public void Dispose()
        {
            if (this.instanceBuffer != null)
                this.instanceBuffer.Dispose();
            this.instanceBuffer = null;
        }

        public void UpdateBuffer()
        {
            Dispose();

            if (this.objectInstances.Count == 0)
                return;

            // Update bufferinformation
            this.vertexBufferDescription.SizeInBytes = this.objectInstances.Values.Count * Marshal.SizeOf(typeof(ObjectInstance));

            // Creating new buffer.
            // Locks the buffer, stored new data and releasing it.
            lock (this.engine.Grafic.Device.ImmediateContext)
            {
                this.instanceBuffer = SharpDX.Direct3D11.Buffer.Create(this.engine.Device, this.objectInstances.Values.ToArray(), this.vertexBufferDescription);
                this.vertexBufferBindingInstacing = new VertexBufferBinding(this.instanceBuffer, Marshal.SizeOf(typeof(ObjectInstance)), 0);
            }
        }

        #endregion Public Methods

        #region Internal Methods

        internal void ToStream(BinaryWriter bw)
        {
            bw.Write("INS".ToCharArray());
            bw.Write((byte)1); // VersionNumber
            bw.Write(this.objectInstances.Count);
            foreach (var item in this.objectInstances)
            {
                // Write the Base Node properties
                item.Key.ToStream(bw);
                // Write the properties which are assigned to the BaseNode
                bw.Write((string)item.Key.Properties);
                // Write the object instance data.
                item.Value.ToStream(bw);
            }
        }

        #endregion Internal Methods

        #region Private Methods

        private static TreeProperties LoadV1(BinaryReader br, Engine engine, string pathToMoxFile, TextureManager textureManager)
        {
            int numberOfInstances = br.ReadInt32();
            TreeProperties properties = new TreeProperties(engine);
            if (File.Exists(pathToMoxFile))
                properties.treeFile = TreeFile.FromFile(engine, pathToMoxFile, textureManager);

            for (int i = 0; i < numberOfInstances; i++)
            {
                BaseNode baseNode = BaseNode.FromStream(br, engine);
                baseNode.Properties = br.ReadString();

                ObjectInstance objInstance = ObjectInstance.FromStream(br);
                properties.objectInstances.Add(baseNode, objInstance);
            }
            properties.UpdateBuffer();
            return properties;
        }

        #endregion Private Methods
    }
}